﻿using SdmxMl.Common;
using SdmxMl.GenericMetadata;
using SdmxMl.Manager;
using SdmxMl.Structure;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Security.Principal;
using System.Text;
using System.Xml;

namespace SdmxMl.Multiplexor
{
    #region Multiplexor connection status

    public enum MultiplexorState
    {
        OkSingle,
        OkMultiplex,
        Unused,
        OnError
    }
    #endregion


    /// <summary>
    /// This class is responsible to access correct registry for artefacts managment 
    /// </summary>
    public class RegistryMultiplex : IRegistry
    {
        #region Internal Members

        /// <summary> NSI user id in SMS system </summary>
        private string uid;
        private bool forceSingleMode;
        /// <summary> If registry is used the configuration must provide the version as key to use.</summary>
        public string vs;

        public ProxyService proxyAdmin;
        private ProxyService proxyDirInfo;
        private ArtefactRefTyped artGroup = null;
        private ArtefactRefTyped artUsers = null;
        private MetadataSetType metaSet;
        public CodeListType ClGroup { get; private set; }

        private Dictionary<string, string> dicoGroupUrn;
        private Dictionary<string, ProxyService> dicoUrnProxy;

        private DataTable dtUrn;


        #endregion

        #region Properties

        public bool IsNewSmsRegistry { get { return proxyAdmin != null && proxyAdmin.useSdmxProxy; } }

        /// <summary> State of the multiplexor </summary>
        public MultiplexorState State { get; private set; }
        /// <summary> True if multiplexor info found in configuration </summary>
        public bool ConfigMultiplexed { get; private set; }
        /// <summary> Provides error message from construction </summary>
        public string ErrorStateMessage { get; private set; }
        /// <summary> The registry proxy to use in DirInfo dialog box </summary>
        public ProxyService DirInfoProxy { get { return proxyDirInfo; } }
        /// <summary> Table of alternates registries url </summary>
        public DataTable TableRegistry { get { return dtUrn; } }
        /// <summary> State of the multiplexor </summary>
        public bool RunningOK { get { return State == MultiplexorState.OkMultiplex || State == MultiplexorState.OkSingle; } }
        /// <summary> Current user is administrator </summary>
        public bool UserIsAdmin { get { return proxyAdmin != null && proxyAdmin.UserIsAdmin; } }

        public DataTable TableGroups
        {
            get
            {
                DataTable tb = new DataTable("UrlTable");
                tb.Columns.Add(new DataColumn("Group", typeof(string)));
                tb.Columns.Add(new DataColumn("Url", typeof(string)));
                tb.Columns.Add(new DataColumn("Name", typeof(string)));
                if (dicoGroupUrn != null && dicoGroupUrn.Count > 0)
                {
                    // Acquire keys and sort them.
                    List<string> grps = new List<string>(dicoGroupUrn.Keys);
                    grps.Sort();

                    // Loop through keys.
                    foreach (string key in grps)
                    {
                        CodeType c = ClGroup.CodeList.LocateItem(key) as CodeType;

                        tb.Rows.Add(new object[] { key, dicoGroupUrn[key], c.Name.ToString() });
                    }
                }
                
                return tb;
            }
        }
        /// <summary> User ID  as in Windows Identity </summary>
        public string UserId
        {
            get { return uid; }
            set
            {
                //TODO: New reg do not modify
                if (value != null)
                    uid = value.ToLower();
            }
        }

        /// <summary> Name associated to registry </summary>
        public string DirInfoRegistryAlias
        {
            get
            {
                string alias = "Undefined";
                if (dtUrn != null && dtUrn.Rows.Count > 0)
                {
                    foreach (DataRow row in dtUrn.Rows)
                        if (row["Url"].ToString().ToLower() == DirInfoProxy.Url)
                        {
                            alias = row["Group"].ToString();
                        }
                }
                return alias;
            }
        }

        /// <summary> Alias name defined for current dirInfo proxy </summary>
        /// <remarks> Assign alias only if proxy selected for dir info is the proxy dedicated 
        /// to group of the user</remarks>
        public string DirInfoAgencyFilter
        {
            get
            {
                string filter = string.Empty;
                // if multiplexed and a standard user logged
                if (State == MultiplexorState.OkMultiplex && UserIsAdmin == false)
                {
                    List<string> listGrp = GetUserGroups(UserId);
                    if (listGrp.Count == 1)
                    {
                        ProxyService r = GetProxyForGroup(listGrp[0]);

                        // Assign alias only if same as dedicated
                        if (r == proxyDirInfo)
                            filter = listGrp[0];
                    }
                }
                return filter;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Internal helper, prepares Mode of function and init registrer(s)
        /// </summary>
        /// <param name="url">Url of admin registry (main one)</param>
        private void init(string url)
        {
            dtUrn = new DataTable("Url");
            dtUrn.Columns.Add(new DataColumn("Group", typeof(string)));
            dtUrn.Columns.Add(new DataColumn("Url", typeof(string)));
            ConfigMultiplexed = false;
            try
            {
                Uri multiplexorUrn = new Uri(url);
            }
            catch
            {
                throw new ApplicationException("Invalid URL received for Multiplexor: [" + url + "]");
            }

            if (ConfigurationManager.AppSettings["SimulatedUser"] == null ||
                ConfigurationManager.AppSettings["SimulatedUser"].ToString().Trim() == string.Empty)
                UserId = WindowsIdentity.GetCurrent().Name;
            else
                UserId = ConfigurationManager.AppSettings["SimulatedUser"].ToString().Trim();
            try
            {
                // Main registry (admin in multiplexor case)
                string tinyUrl = ConfigurationManager.AppSettings["TinyRegistryUrl"].ToString().Trim().ToLower();
                string[] arr = tinyUrl.Split(';');
                tinyUrl = arr[arr.Length - 1].ToLower();
                forceSingleMode = url.ToLower() != tinyUrl;
                string v = ConfigurationManager.AppSettings["TinyRegistryVers"].ToString().Trim();
                vs = v[0] == '4' ? v : "44" + v;
            }
            catch
            {
                throw new ApplicationException("Unable to access registry information");
            }

            string groupFullId = string.Empty;
            string usersFullId = string.Empty;

            // Now test access Admin web service
            try
            {
                proxyAdmin = new ProxyService(url, vs, UserId);
                proxyAdmin.Test();
                proxyDirInfo = proxyAdmin; // default same
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Unable to access administration registry. Reason: " + ex.Message);
            }

            // Now access id info to get group agency information
            if (ConfigurationManager.AppSettings["MultiplexorGroupFullId"] == null ||
                ConfigurationManager.AppSettings["MultiplexorGroupFullId"].ToString().Trim() == string.Empty)
            {
                State = MultiplexorState.OkSingle;
            }
            else
            {
                // We got tags for Multiplex managment
                ConfigMultiplexed = true;

                // Do we have request for a registry outside of Multiplexed ones ?
                if (forceSingleMode)
                    State = MultiplexorState.OkSingle;
                else
                {
                    try
                    {
                        // Fetch groups and users of multiplexed registries
                        groupFullId = ConfigurationManager.AppSettings["MultiplexorGroupFullId"].ToString().Trim();

                        // Expected in Codelist type
                        artGroup = new ArtefactRefTyped(SdmxArtefactType.CodeLists, groupFullId);
                    }
                    catch
                    {
                        throw new ApplicationException("Invalid Multiplexor group full ID received: [" + groupFullId + "]");
                    }

                    // Now access id info to get system users information
                    try
                    {
                        usersFullId = ConfigurationManager.AppSettings["MultiplexorUsersFullId"].ToString().Trim();

                        // Expected in generic metadataset type
                        artUsers = new ArtefactRefTyped(SdmxArtefactType.MetadataSet, usersFullId);
                    }
                    catch
                    {
                        throw new ApplicationException("Invalid Multiplexor group full ID received: [" + groupFullId + "]");
                    }

                    try
                    {

                        CreateGroupInfo();

                        State = MultiplexorState.OkMultiplex;
                        SetUserDirInfo();
                    }
                    catch
                    {
                        State = MultiplexorState.OnError;
                        throw;
                         
            
                    }
                }
            }
            
            FillTableRegistry();
        }

        private void SetUserDirInfo()
        {
            List<string> listGrp = GetUserGroups(UserId);
            if (listGrp.Count > 0)
                proxyDirInfo = GetProxyForGroup(listGrp[0]);
        }

        /// <summary>Parametrized C'tor,admin registry url is provided</summary>
        public RegistryMultiplex(string url)
        {
            dicoGroupUrn = new Dictionary<string, string>();
            dicoUrnProxy = new Dictionary<string, ProxyService>();

            if (string.IsNullOrEmpty(url))
                State = MultiplexorState.Unused;
            else
            {
                try
                {
                    init(url);
                }
                catch (Exception ex)
                {
                    State = MultiplexorState.OnError;
                    ErrorStateMessage = ex.Message;
                }
            }
        }

        /// <summary>Default C'tor, uses config file for retrieving admin registry url </summary>
        public RegistryMultiplex()
        {
            dicoGroupUrn = new Dictionary<string, string>();
            dicoUrnProxy = new Dictionary<string, ProxyService>();
            
            // Get url of admin repository via configuration file
            string url = string.Empty;
            if (ConfigurationManager.AppSettings["TinyRegistryUrl"] == null || 
                ConfigurationManager.AppSettings["TinyRegistryUrl"].ToString().Trim() == string.Empty)
                State = MultiplexorState.Unused;
            else
            {
                try
                {
                    string tinyUrl = ConfigurationManager.AppSettings["TinyRegistryUrl"].ToString().Trim();
                    string[] arr = tinyUrl.Split(';');
                    url = arr[arr.Length - 1].ToLower();
                }
                catch
                {
                    State = MultiplexorState.OnError;
                    ErrorStateMessage = "Invalid URL received for Main registry: [" + url + "]";
                }
                try
                {
                    init(url);
                }

                catch (Exception ex)
                {
                    State = MultiplexorState.OnError;
                    ErrorStateMessage = ex.Message;
                }
            }
        }

        #endregion

        #region DirInfo proxy setting

        /// <summary>Requests for changing dirInfo proxy </summary>
        /// <param name="url">the url of new one</param>
        /// <remarks>Change only if in list of group registries</remarks>
        public void SetDirInfoProxy(string url)
        {
            if (State == MultiplexorState.OkMultiplex)
            {
                url = url.ToLower();

                // Is it part of group registries
                if (dicoUrnProxy.ContainsKey(url))
                {
                    proxyDirInfo = dicoUrnProxy[url];
                    if (proxyDirInfo == null)
                    {
                         try
                         {
                             proxyDirInfo = new ProxyService(url, vs, UserId);
                             proxyDirInfo.Test();
                             dicoUrnProxy[url] = proxyDirInfo;
                         }                    
                         catch (Exception ex)
                         {
                             SetUserDirInfo();
                             throw new ApplicationException(string.Format("Unable to access registry: {0}. Reason: {1}", url, ex.Message));
                         }
                    }
                }
            }
        }
        #endregion

        #region Groups

        /// <summary>
        /// Cteates groups keys & proxy keys according to artefacts information
        /// </summary>
        private void CreateGroupInfo()
        {
            SmManager sm = new SmManager();

            // Access group list
            XmlDocument xDoc = proxyAdmin.GetArtefact(artGroup.Type.ToString(), artGroup.FullIdent);
            if (xDoc == null)
                throw new ApplicationException("Unable to obtain group list from administration repository. [" + artGroup.FullIdent + "]");
            sm.LoadSdmxDocument(null, true, xDoc);
            ClGroup = sm.GetArtefact(artGroup.Type, artGroup, true) as CodeListType;
            if (ClGroup == null)
                throw new ApplicationException("Unable to obtain group list from administration repository. [" + artGroup.FullIdent + "]");

            xDoc = proxyAdmin.GetArtefact(artUsers.Type.ToString(), artUsers.FullIdent);
            if (xDoc == null)
                throw new ApplicationException("Unable to obtain user list from administration repository. [" + artUsers.FullIdent + "]");
            sm.LoadSdmxDocument(null, true, xDoc);
            metaSet = sm.GetArtefact(artUsers.Type, artUsers, true) as MetadataSetType;
            if (metaSet == null)
                throw new ApplicationException("Unable to obtain user list from administration repository. [" + artUsers.FullIdent + "]");

            metaSet.AttributeValueSetList.Sort(delegate(AttributeValueSetType p1, AttributeValueSetType p2)
            { return p1.TargetValuesTypeList[0].Value.CompareTo(p2.TargetValuesTypeList[0].Value); });

            // First entry in dicoUrn is for Admin registry proxy
            dicoUrnProxy.Add(proxyAdmin.Url.ToLower(), proxyAdmin);

            // prepare dico of groups & urn
            foreach (CodeType c in ClGroup.CodeList)
            {
                Annotation a = c.AnnotationList.GetAnnotationType("REPOSITORY");
                if (a == null)
                    throw new ApplicationException("Unexpected, no registry information for group: " + c.Id);
                if (a.Url == null)
                    throw new ApplicationException("Unexpected, no registry URL information for group: " + c.Id);

                // Do not yet instantiate others proxy
                if (dicoUrnProxy.ContainsKey(a.Url.ToString().ToLower()) == false)
                    dicoUrnProxy.Add(a.Url.ToString().ToLower(), null);

                dicoGroupUrn.Add(c.Id, a.Url.ToString().ToLower());
            }
        }

        /// <summary>
        /// Construct a dictionary for holding dirInfo of all supported registries
        /// </summary>
        /// <returns>the dictionary</returns>
        public Dictionary<string, DirInfoItem> CreateDicoDirInfoMux()
        {
            Dictionary<string, DirInfoItem> dicoDirInfo = new Dictionary<string, DirInfoItem>();

            // Proxy admin exists in all modes
            DirInfoItem diproxy = new DirInfoItem(proxyAdmin);
            dicoDirInfo.Add(proxyAdmin.Url.ToLower(), diproxy);

            // Supported Multiplexed registries to add 
            if (State == MultiplexorState.OkMultiplex)
            {
                // Ensures all proxy created
                foreach (KeyValuePair<string, ProxyService> kvp in dicoUrnProxy)
                {
                    ProxyService rp = kvp.Value;
                    if (rp == null)
                        rp = new ProxyService(kvp.Key, vs, UserId);

                    if (rp != proxyAdmin)   // Already registered 
                    {
                        DirInfoItem di = new DirInfoItem(rp);
                        dicoDirInfo.Add(kvp.Key, di);
                    }
                }
            }

            // Now if groups defined create a dico with group id key
            Dictionary<string, DirInfoItem> dicoGroupDirInfo = new Dictionary<string, DirInfoItem>();

            // The unknown groups are directed in the admin registry
            dicoGroupDirInfo.Add("+default", diproxy); // The key cannot be a valid group key
            // Supported Multiplexed registries to add 
            if (State == MultiplexorState.OkMultiplex)
            {
                foreach (KeyValuePair<string, string> kv in dicoGroupUrn)
                    dicoGroupDirInfo.Add(kv.Key, dicoDirInfo[kv.Value]);
            }

            return dicoGroupDirInfo;
        }

        

        /// <summary>Get group keys associated to specified user</summary>
        /// <param name="userId">the user ident. </param>
        /// <returns>a list of group keys</returns>
        public List<string> GetUserGroups(string userId)
        {
            List<string> lug = new List<string>();

            foreach (AttributeValueSetType atv in metaSet.AttributeValueSetList)
            {
                if (atv.TargetValuesTypeList[0].Value.ToLower() == userId.ToLower())
                {
                    ReportedAttributeTypeList rpList = new ReportedAttributeTypeList();
                    atv.ReportedAttributeList.GetReportedAttributeListForConcept("GROUP", rpList);
                    foreach (ReportedAttributeType rp in rpList)
                    {
                        string grp = rp.ReportedAttributeList.GetReportedAttributeOfConcept("UGRP_ID").Value.GetFirst();
                        if (string.IsNullOrEmpty(grp) == false)
                            lug.Add(grp);
                    }
                    break;
                }
            }

            return lug;
        }
        /// <summary>Get Mail of specified user</summary>
        /// <param name="userId">the user ident. </param>
        /// <returns>eMail string or empty string if not found</returns>
        public string GetMailOfUser(string userId)
        {
            string mail = string.Empty;

            // Do we have meta info about users ?
            if (metaSet != null)
            {
                foreach (AttributeValueSetType atv in metaSet.AttributeValueSetList)
                {
                    // Can we locate required user
                    if (atv.TargetValuesTypeList[0].Value.ToLower() == userId.ToLower())
                    {
                        ReportedAttributeType rp = atv.ReportedAttributeList.GetReportedAttributeOfConcept("UEMAIL");
                        if (rp != null)
                        {
                            mail = rp.InternationalizedValue.ToString();
                        }
                        break;
                    }
                }
            }

            return mail;
        }

        /// <summary> Get metaset rows matching users of specified group </summary>
        /// <param name="groupId"> The group key.</param>
        /// <returns>list of matching rows</returns>
        public List<AttributeValueSetType> GetGroupUserInfo(string groupId)
        {
            List<AttributeValueSetType> lug = new List<AttributeValueSetType>();

            foreach (AttributeValueSetType atv in metaSet.AttributeValueSetList)
            {
                ReportedAttributeTypeList rpList = new ReportedAttributeTypeList();
                atv.ReportedAttributeList.GetReportedAttributeListForConcept("GROUP", rpList);
                foreach (ReportedAttributeType rp in rpList)
                {
                    string grp = rp.ReportedAttributeList.GetReportedAttributeValueOfConcept("UGRP_ID").GetFirst();
                    if (grp == groupId)
                    {
                        lug.Add(atv);
                        break;
                    }
                }
            }
            
            return lug;
        }

        /// <summary>Get groups associated to specified registry</summary>
        /// <param name="url">Url of registry to look at</param>
        /// <returns>the list of group keys</returns>
        public List<string> GetRegistryGroups(string url)
        {
            List<string> lrg = new List<string>();
            url = url.ToLower();
            foreach (KeyValuePair<string, string> grpUrl in dicoGroupUrn)
                if (grpUrl.Value == url)
                    lrg.Add(grpUrl.Key);

            return lrg;
        }

        /// <summary>Get proxy of registry associated to group </summary>
        /// <param name="groupInfo">Can be the group key or an artefact fullId</param>
        /// <returns>the proxy associated or null if not found</returns>
        public ProxyService GetProxyForGroup(string groupInfo)
        {     
             ProxyService reg = null;
             if (string.IsNullOrEmpty(groupInfo) == false)
             {
                 if (State == MultiplexorState.OkSingle)
                     reg = proxyAdmin;
                 else if (State == MultiplexorState.OkMultiplex)
                 {
                     // Get group (AgencyId) from fullId
                     string[] arr = groupInfo.Split('+');
                     if (arr.Length != 3 && arr.Length != 1)
                         throw new ApplicationException("Invalid Artefact FullId: " + groupInfo);
                     string agencyId = arr[0];
                     if (dicoGroupUrn.ContainsKey(agencyId))
                     {
                         reg = dicoUrnProxy[dicoGroupUrn[agencyId]];
                         if (reg == null)
                         {
                             try
                             {
                                 reg = new ProxyService(dicoGroupUrn[agencyId], vs, UserId);
                                 reg.Test();
                                 dicoUrnProxy[dicoGroupUrn[agencyId]] = reg;
                             }

                             catch (Exception ex)
                             {
                                 reg = null;
                                 throw new ApplicationException(string.Format("Unable to access registry of group {0}. Reason: {1}", agencyId, ex.Message));
                             }
                         }

                     }
                     else if (arr.Length == 3)  // If it was a fillId of an artefact
                         reg = proxyAdmin;      // default unknown group to admin reg
                 }
             }

            return reg;
        }


        /// <summary>Get all the registry proxies </summary>
        /// <returns>Returns all the registry proxies related to the multiplex</returns>
        public List<ProxyService> GetProxies()
        {
            List<ProxyService> result = new List<ProxyService>();

            if (State == MultiplexorState.OkMultiplex)
            {
                // Iterate on all prxies
                foreach (KeyValuePair<string, ProxyService> kvp in dicoUrnProxy)
                {
                    ProxyService rp = kvp.Value;
                    if (rp == null)
                    {
                        rp = new ProxyService(kvp.Key, vs, UserId);
                    }
                    result.Add(rp);
                }
            }
            else if (State == MultiplexorState.OkSingle)
            {
                result.Add(proxyAdmin);
            }

            return result;
        }

        #endregion

        #region Public access to Registry service methods

        public string GetLastVersion(string arType, string fullIdent)
        {
            string v = "1.0";
            ProxyService reg = GetProxyForGroup(fullIdent);
            if (reg != null && reg.State == RepositoryState.Ok)
                v = reg.GetLastVersion(arType, fullIdent);
            return v;
        }

        public XmlDocument GetArtefact(string arType, string fullIdent)
        {
            XmlDocument xDoc = null;
            ProxyService reg = GetProxyForGroup(fullIdent);
            if (reg != null && reg.State == RepositoryState.Ok)
                xDoc = reg.GetArtefact(arType, fullIdent);

            return xDoc;
        }

        public bool ExistArtefact(string arType, string fullIdent)
        {
            bool exist = false;
            ProxyService reg = GetProxyForGroup(fullIdent);
            if (reg != null && reg.State == RepositoryState.Ok)
                exist = reg.ExistArtefact(arType, fullIdent);

            return exist;
        }

        public void DeleteArtefact(string arType, string fullIdent)
        {
            ProxyService reg = GetProxyForGroup(fullIdent);
            if (reg != null && reg.State == RepositoryState.Ok)
                reg.DeleteArtefact(arType, fullIdent);
        }

        public void SetDisseminationStatus(string artType, string fullIdent, string diffusionCd)
        {
            ProxyService reg = GetProxyForGroup(fullIdent);
            if (reg != null && reg.State == RepositoryState.Ok)
                reg.SetDisseminationStatus(artType, fullIdent, diffusionCd);
        }

        public void SubmitArtefactBackup(string arType, string fullIdent, XmlDocument xDoc)
        {
            ProxyService reg = GetProxyForGroup(fullIdent);
            if (reg != null && reg.State == RepositoryState.Ok)
                reg.SubmitArtefactBackup(arType, fullIdent, xDoc.DocumentElement);
        }

        public void SubmitArtefact(string arType, string fullIdent, XmlDocument xDoc)
        {
             ProxyService reg = GetProxyForGroup(fullIdent);
             if (reg != null && reg.State == RepositoryState.Ok)
                 reg.SubmitArtefact(arType, fullIdent, xDoc.DocumentElement);
       }

        public void CheckOut(string arType, string fullIdent)
        {
            ProxyService reg = GetProxyForGroup(fullIdent);
            if (reg != null && reg.State == RepositoryState.Ok)
                reg.CheckOut(arType, fullIdent);
        }

        public void UndoCheckOut(string arType, string fullIdent)
        {
            ProxyService reg = GetProxyForGroup(fullIdent);
            if (reg != null && reg.State == RepositoryState.Ok)
                reg.UndoCheckOut(arType, fullIdent);
        }

        /// <summary>Retrieves information list of artefact for specified type </summary>
        /// <param name="arType">Artefact type</param>
        /// <param name="agencyId">Agency (group ID)</param>
        /// <returns>A dataset holding the information table</returns>
        public System.Data.DataSet DirInfo(string arType, string agencyId)
        {
            DataSet ds = new DataSet();
            ProxyService reg = GetProxyForGroup(agencyId);
            if (reg != null && reg.State == RepositoryState.Ok)
                ds = reg.DirInfo(arType);
                
            return ds;
        }
        /// <summary>Retrieves information list of artefact for specified type in all supported registries </summary>
        /// <param name="arType">Artefact type</param>
        /// <returns>A dataset holding the information table</returns>
        public System.Data.DataSet DirInfo(string arType)
        {
            DataSet ds = new DataSet();
            ProxyService reg = null;
            if (State == MultiplexorState.OkMultiplex)
            {
                List<string> listUrn = new List<string>();
                foreach (KeyValuePair<string, string> kvp in dicoGroupUrn)
                {
                    if (listUrn.Contains(kvp.Value) == false)
                    {
                        listUrn.Add(kvp.Value);
                        DataSet dsReg = null;
                        reg = GetProxyForGroup(kvp.Key);
                        if (reg != null && reg.State == RepositoryState.Ok)
                            dsReg = reg.DirInfo(arType);

                        if (dsReg != null && dsReg.Tables.Count > 0)
                        {
                            if (ds.Tables.Count == 0)
                                ds = dsReg.Copy();
                            else
                            {
                                for (int i = 0; i < dsReg.Tables[0].Rows.Count; ++i)
                                {
                                    try
                                    {
                                        ds.Tables[0].ImportRow(dsReg.Tables[0].Rows[i]);
                                    }
                                    catch (Exception ex)
                                    {
                                       throw new ApplicationException("Unexpected duplicated artefact identification: " + dsReg.Tables[0].Rows[i]["ID"].ToString() + Environment.NewLine, ex); 
                                    }
                                }
                            }
                        }

                    }
                }
            }
            else
            {
                ds = proxyAdmin.DirInfo(arType);
            }

            return ds;
        }

        public bool IsArtefactUpdated(string arType, string fullIdent, DateTime dtSource)
        {
            bool updated = false;
            DataSet ds = DirInfo(arType, fullIdent);
            ArtefactRefBase arb = new ArtefactRefBase(fullIdent);
            if (ds != null && ds.Tables.Count > 0)
            {
                foreach (DataRow row in ds.Tables[0].Rows)
                {
                    if (arb.AgencyId == row["AI"].ToString() && arb.Id == row["Id"].ToString() && arb.Version == row["Ver"].ToString())
                    {
                        string dtStr = row["LU"].ToString();
                      //  DateTime dt = Convert.ToDateTime(row["LU"].ToString());
                        DateTime dt = Convert.ToDateTime(dtStr);
                        updated = dt > dtSource;
                        break;
                    }
                }
            }
            return updated;
        }


        /// <summary>Determines if backup info exists</summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <returns>True if exists</returns>
        public bool HasBackup(string ar, string fullId)
        {
            bool bReturn = false;

            return (bReturn);
        }

        /// <summary> Get list of backup of specified artefact</summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <returns>A dataset holding backup list information</returns>
        public DataSet GetBackupList(string ar, string fullId)
        {
            DataSet dsReturn = new DataSet();

            return (dsReturn);
        }

        /// <summary> Retrieve a backuped Artefact document </summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <param name="versionNumber">Backup number</param>
        /// <returns></returns>
        public XmlDocument GetArtefactBackup(string ar, string fullId, string versionNumber)
        {
            XmlDocument xDoc = null;


            return xDoc;
        }

        #endregion

        #region ProxyTableInfo

        /// <summary>
        /// Creates a table with alias and url for each accessible registry 
        /// (according to actual function mode)
        /// </summary>
        /// <returns>Data Table with registries list</returns>
        public DataTable FillTableRegistry()
        {
            dtUrn.Rows.Clear();
            if (State == MultiplexorState.OkSingle)
            {
                try
                {
                    string tinyUrl = ConfigurationManager.AppSettings["TinyRegistryUrl"].ToString().Trim();
                    string[] arr = tinyUrl.Split(';');
                    string url = arr[arr.Length - 1].ToLower();
                    string group = arr.Length > 1 ? arr[0] : "Main";
                    dtUrn.Rows.Add(new object[] { group, url });

                    if (ConfigurationManager.AppSettings["TinyRegistryAlternateUrl"] != null)
                    {
                        // Presented as Usage;http;usage;http
                        url = ConfigurationManager.AppSettings["TinyRegistryAlternateUrl"].ToString().Trim();
                        arr = url.Split(';');
                        int index = 0;
                        if (arr.Length > 0 && (arr.Length % 2) == 0)
                        {
                            while (index < arr.Length)
                            {
                                dtUrn.Rows.Add(new object[] { arr[index], arr[index + 1].ToLower() });
                                index += 2;
                            }
                        }
                    }
                }
                catch { }
            }
            else if (State == MultiplexorState.OkMultiplex)
            {
                foreach (KeyValuePair<string, ProxyService> up in dicoUrnProxy)
                {
                    List<string> listGroups = GetRegistryGroups(up.Key);
                    dtUrn.Rows.Add(new object[] { string.Join(";", listGroups.ToArray()), up.Key});
                }
            }
            return dtUrn;

        }

        #endregion

        #region Org Scheme generation

        public void DistributeOrgScheme()
        {
            if (State == MultiplexorState.OkMultiplex && UserIsAdmin)
            {
                OrganisationSchemesType osFull = CreateOrgSchemeSkeleton();
                osFull.Id = "ORG_EDI";
                osFull.Name.UpdateDefaultText("SFSO SMS groups for Maintenance contact");

                // For each registry
                string[] arrUrn = new string[dicoUrnProxy.Keys.Count];
                dicoUrnProxy.Keys.CopyTo(arrUrn, 0);
                foreach (string url in arrUrn)
                {
                    // Create orgScheme skeleton
                    OrganisationSchemesType os = CreateOrgSchemeSkeleton();

                    // Create groups and associated users of this reg
                    List<string> listGroups = GetRegistryGroups(url);
                    foreach (string grp in listGroups)
                    {
                        CodeType c = ClGroup.CodeList.LocateItem(grp) as CodeType;
                        if (c != null)
                        {
                            OrganisationType o = new OrganisationType();
                            o.Id = c.Id;
                            o.Name = c.Name.Clone();
                            // add users
                            FillOrgUserInfo(o);
                            os.Agencies.Add(o);
                            osFull.Agencies.Add(o);
                        }
                    }

                    // Persist
                    if (listGroups.Count > 0)
                    {
                        ProxyService reg = GetProxyForGroup(listGroups[0]);
                        if (reg == null)
                            throw new ApplicationException("Unable to retrieve proxy for group " + listGroups[0]);
                        if (reg.State != RepositoryState.Ok)
                            throw new ApplicationException("Repository proxy on error for for group " + listGroups[0]);
                        if (reg.ExistArtefact(SdmxArtefactType.OrganisationSchemes.ToString(), os.FullIdent))
                            reg.CheckOut(SdmxArtefactType.OrganisationSchemes.ToString(), os.FullIdent);
                        SmManager sm = new SmManager();
                        sm.PutArtefact(SdmxArtefactType.OrganisationSchemes, os);
                        reg.SubmitArtefact(SdmxArtefactType.OrganisationSchemes.ToString(), os.FullIdent, sm.GetAsDocument().DocumentElement);
                    }
                }
                // Persist full
                SmManager smFull = new SmManager();
                osFull.Agencies.Sort(delegate(ISdmxHierarchyListItem p1, ISdmxHierarchyListItem p2)
                { return p1.Id.ToString().CompareTo(p2.Id.ToString()); });
                smFull.PutArtefact(SdmxArtefactType.OrganisationSchemes, osFull);
                if (proxyAdmin.ExistArtefact(SdmxArtefactType.OrganisationSchemes.ToString(), osFull.FullIdent))
                    proxyAdmin.CheckOut(SdmxArtefactType.OrganisationSchemes.ToString(), osFull.FullIdent);
                proxyAdmin.SubmitArtefact(SdmxArtefactType.OrganisationSchemes.ToString(), osFull.FullIdent, smFull.GetAsDocument().DocumentElement);


            }
        }

        private OrganisationSchemesType CreateOrgSchemeSkeleton()
        {
            OrganisationSchemesType os = new OrganisationSchemesType();
            List<string> lg = GetUserGroups(UserId);
            if (lg.Count != 1)
                throw new ApplicationException("Unable to determine Admin group of user " + UserId);
            os.Id = "ORG_NSI";
            os.AgencyId = lg[0];
            os.Version = "1.0";
            os.Name.UpdateDefaultText("NSI group and users of the repository");
            os.ValidFrom = DateTime.Now.ToString("yyyy-MM-dd");
            return os;

        }


        public void FillOrgUserInfo(OrganisationType o)
        {
            foreach (AttributeValueSetType atv in metaSet.AttributeValueSetList)
            {
                // User in required group ?
                ReportedAttributeTypeList rpList = new ReportedAttributeTypeList();
                atv.ReportedAttributeList.GetReportedAttributeListForConcept("GROUP", rpList);
                foreach (ReportedAttributeType rp in rpList)
                {
                    string grp = rp.ReportedAttributeList.GetReportedAttributeValueOfConcept("UGRP_ID").GetFirst();
                    if (grp == o.Id)
                    {
                        ContactType contact = new ContactType();
                        FillContact(contact, atv, rp);
                        if (contact.Role.ToString().IndexOf("Maintenance") >= 0)
                        {
                            if (o.MaintenanceContact.IsEmpty == false)
                                throw new ApplicationException("Error more than one Maintenance contact for group: " + o.Id);
                            else
                                FillContact(o.MaintenanceContact, atv, rp);
                        }
                        else
                            o.OtherContactList.Add(contact);
                        
                    }
                }
            }

            if (o.MaintenanceContact.IsEmpty)
            {
                throw new ApplicationException("Error no Maintenance contact for group: " + o.Id);
            }
            if (o.OtherContactList != null && o.OtherContactList.Count > 0)
            {
                o.OtherContactList.Sort(delegate(ContactType p1, ContactType p2)
                { return p1.Name.ToString().CompareTo(p2.Name.ToString()); });
            }
        }

        private void FillContact(ContactType contact, AttributeValueSetType atv, ReportedAttributeType rp)
        {
            string winIdentity = atv.TargetValuesTypeList[0].Value.ToLower();
            string[] arr = winIdentity.Split('\\');
            // User ID
            contact.Id = arr[arr.Length - 1].ToLower();
            contact.Department.UpdateDefaultText(arr[0] + "\\");

            // Determine if maintenance or Other contact
            string role = rp.ReportedAttributeList.GetReportedAttributeValueOfConcept("UROLE").GetFirst();
            contact.Role.UpdateDefaultText(role);
            contact.Name.UpdateDefaultText(atv.ReportedAttributeList.GetReportedAttributeValueOfConcept("UNAME").GetFirst());
            contact.ContactInfo.EmailList.Add(atv.ReportedAttributeList.GetReportedAttributeValueOfConcept("UEMAIL").GetFirst());
            contact.ContactInfo.TelephoneList.Add(atv.ReportedAttributeList.GetReportedAttributeValueOfConcept("UPHONE").GetFirst());
        }

        #endregion

        #region All in One

        public void AllInOneRegistry(string emptyRegistryUrl)
        {
            if (State == MultiplexorState.OkMultiplex && UserIsAdmin)
            {
                // Create the destination proxy
                ProxyService rpDest = new ProxyService(emptyRegistryUrl, vs, UserId);

                // for all proxies of multiplexor
                // Ensures all proxy created
                foreach (KeyValuePair<string, ProxyService> kvp in dicoUrnProxy)
                {
                    ProxyService rp = kvp.Value;
                    if (rp == null)
                        rp = new ProxyService(kvp.Key, vs, UserId);

                    //    For all artefacts types
                    foreach (SdmxArtefactType atype in Enum.GetValues(typeof(SdmxArtefactType)))
                    {
                        if (atype == SdmxArtefactType.None)
                            continue;

                        if (rpDest.State != RepositoryState.Ok)
                            throw new ApplicationException("Destination Repository not Ready !");

                        DataSet dsInfo = rp.DirInfo(atype.ToString());
                        if (dsInfo != null && dsInfo.Tables.Count > 0)
                        {
                            //       for all artefact
                            foreach (DataRow row in dsInfo.Tables[0].Rows)
                            {
                                StringBuilder sb = new StringBuilder();
                                sb.Append(row[2].ToString()).Append('+');
                                sb.Append(row[0].ToString()).Append('+');
                                sb.Append(row[1].ToString());
                                string fullId = sb.ToString();

                                if (rpDest.ExistArtefact(atype.ToString(), fullId) == false)
                                {                                    
                                    XmlDocument xDoc = rp.GetArtefact(atype.ToString(), fullId);
                                    rpDest.SubmitArtefact(atype.ToString(), fullId, xDoc.DocumentElement);
                                }
                            }
                        }
                    }
                }

            }
        }

        #endregion

        /// <summary> Update text part of the specified finalized maintenable artefact </summary>
        /// <param name="arType">Type of artefact</param>
        /// <param name="fullIdent">AgencyId + Id + version</param>
        /// <param name="xDoc">the maintenable artefact sdmx document</param>
        public void UpdateFinalizedArtefact(string arType, string fullIdent, XmlDocument xDoc)
        {
            proxyAdmin.UpdateFinalizedArtefact(arType, fullIdent, xDoc);
        }

        // Not implemented in tinyRegistry
        public void UpdateOwner(string artType, string fullId, string ownerId)
        {
            proxyAdmin.UpdateOwner(artType, fullId, ownerId);
        }

        public List<string> UserOwnerList()
        {
            return proxyAdmin.UserOwnerList();
        }



        public string ConnectedInfo
        {
            get
            {
                string url = DirInfoProxy != null ? DirInfoProxy.Url : string.Empty;
                string state = State == MultiplexorState.Unused ? State.ToString() : DirInfoProxy.State.ToString();
                return string.Format("Registry:{0} - {1}, State: {2}", Mux.Instance.DirInfoRegistryAlias, url, state);
            }
        }

        public Ch.Admin.Bfs.Sms.Sdmx.Registry.Rights.ISecuredArtefactRegistry GetNewRegistry()
        {
            return proxyAdmin.GetNewRegistry();
        }

        public Ch.Admin.Bfs.Sms.Sdmx.Registry.Proxies.WcfAdministrationProxy GetAdminRegistry()
        {
            return proxyAdmin.GetAdminRegistry();
        }

        public Ch.Admin.Bfs.Sms.Sdmx.Registry.Proxies.WcfNotificationsProxy GetNotificationRegistry()
        {
            return proxyAdmin.GetNotificationRegistry();
        }
    }
}
